package cn.edu.fzu.homemaking.common;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

import cn.edu.fzu.homemaking.net.LocalHost;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.joda.time.DateTime;

import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;

/**
 * 返回一个基于本机网络地址和当前时间的唯一流水号
 */
public final class Sn {

    private static final AtomicInteger sequence = new AtomicInteger();
    private static final byte[]        ip       = ip();

    private Sn() {
    }


    public static final String next() {
        return next(System.currentTimeMillis());
    }


    /** @param time 当前时间戳 **/
    public static final String next(long time) {
        byte[] data = new byte[14];
        System.arraycopy(ip, 0, data, 0, 4);
        // 时间戳尚不足以占用 Long 的全部 8 个字节, 其高位 2 个字节是空闲的, 可以忽略
        System.arraycopy(Longs.toByteArray(time), 2, data, 4, 6);
        System.arraycopy(Ints.toByteArray(sequence.getAndIncrement()), 0, data, 10, 4);
        return Hex.encodeHexString(data);
    }


    private static final byte[] ip() {
        return LocalHost.getIpv4() == null ? Ints.toByteArray(ThreadLocalRandom.current().nextInt())
                : LocalHost.getAddress();
    }


    /** 从流水号解析出 ip, 时间, 顺序号 **/
    public static final String parse(String sn) throws DecoderException, UnknownHostException {
        byte[] data = Hex.decodeHex(sn);
        byte[] ip = new byte[4];
        System.arraycopy(data, 0, ip, 0, 4);
        byte[] millis = new byte[8];
        System.arraycopy(data, 4, millis, 2, 6);
        byte[] seq = new byte[4];
        System.arraycopy(data, 10, seq, 0, 4);

        StringBuilder buff = new StringBuilder(70);
        buff.append("{\"ip\":\"").append(InetAddress.getByAddress(ip).getHostAddress()).append("\",");
        buff.append("\"time\":\"")
                .append((new DateTime(Longs.fromByteArray(millis))).toString("yyyy-MM-dd HH:mm:ss.SSS")).append("\",");
        buff.append("\"seq\":").append(Ints.fromByteArray(seq)).append('}');
        return buff.toString();
    }
}
